/* Copyright (c) 2008, 2009, 2010, 2011 Damian Kmiecik
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

#include <avr/io.h>
#include <inttypes.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <string.h>
#include <avr/wdt.h>
#include <inttypes.h>
#include <util/delay.h>
#include <stdio.h>
#include "main.h"
#include "stdout.h"
#include "1wire.h"

#ifdef USE_1WIRE

#ifndef OW_DEBUG
	#define OW_DEBUG(...)
#endif

#define OW_LOW	OW_DDR |= _BV(OW_PIN)
#define OW_HI	OW_DDR &= ~_BV(OW_PIN)
#define OW		(OW_PINP & _BV(OW_PIN))

void OWWriteBit(uint8_t bit_value);
uint8_t OWReadBit(void);
uint8_t OWSearch(void);

uint8_t ROM_NO[8];
uint8_t LastDiscrepancy;
uint8_t LastFamilyDiscrepancy;
uint8_t LastDeviceFlag;
uint8_t crc8;
uint8_t OW_TABLE[OW_TABLE_SIZE][8];
uint8_t OW_cnt = 0;

int8_t OWReadTemperature(const uint8_t addr)
{
	OWReset();
	OWWriteByte(0x55);
	for (uint8_t i = 0; i < 8; i++)
		OWWriteByte(OW_TABLE[addr][i]);
	OWWriteByte(0x44);
//	_delay_ms(200);
	OWReset();
	OWWriteByte(0x55);
	for (uint8_t i = 0; i < 8; i++)
		OWWriteByte(OW_TABLE[addr][i]);
	OWWriteByte(0xBE);
	uint8_t lsb = OWReadByte();
	uint8_t msb = OWReadByte();
	OWReset();
	msb <<= 4;
	lsb >>= 4;
	lsb = lsb | msb;
	return lsb;
}

void OWFindAll(void)
{
	// find ALL 1WIRE devices
	uint8_t rslt = OWFirst();
	if (rslt) {
		while (rslt) {
			memcpy(OW_TABLE[OW_cnt], &ROM_NO[0], 8);
			// print device found
			OW_DEBUG("%02hu: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
				OW_cnt, OW_TABLE[OW_cnt][7], OW_TABLE[OW_cnt][6], OW_TABLE[OW_cnt][5], OW_TABLE[OW_cnt][4],
				OW_TABLE[OW_cnt][3], OW_TABLE[OW_cnt][2], OW_TABLE[OW_cnt][1], OW_TABLE[OW_cnt][0]
			);
			OW_cnt++;
			rslt = OWNext();
		}
	}
}

uint8_t OWFirst(void)
{
	// reset the search state
	LastDiscrepancy = 0;
	LastDeviceFlag = FALSE;
	LastFamilyDiscrepancy = 0;
	return OWSearch();
}

uint8_t OWNext(void)
{
	// leave the search state alone
	return OWSearch();
}

uint8_t OWSearch(void)
{
	uint8_t id_bit_number;
	uint8_t last_zero, rom_byte_number, search_result;
	uint8_t id_bit, cmp_id_bit;
	unsigned char rom_byte_mask, search_direction;

	// initialize for search
	id_bit_number = 1;
	last_zero = 0;
	rom_byte_number = 0;
	rom_byte_mask = 1;
	search_result = 0;
	crc8 = 0;

	// if the last call was not the last one
	if (!LastDeviceFlag) {
		// 1-Wire reset
		if (!OWReset()) {
			// reset the search
			LastDiscrepancy = 0;
			LastDeviceFlag = FALSE;
			LastFamilyDiscrepancy = 0;
			return FALSE;
		}

		// issue the search command
		OWWriteByte(0xF0);

		// loop to do the search
		do {
			// read a bit and its complement
			id_bit = OWReadBit();
			cmp_id_bit = OWReadBit();

			// check for no devices on 1-wire
			if ((id_bit == 1) && (cmp_id_bit == 1))
				break;
			else {
				// all devices coupled have 0 or 1
				if (id_bit != cmp_id_bit)
					search_direction = id_bit; // bit write value for search
				else {
					// if this discrepancy if before the Last Discrepancy
					// on a previous next then pick the same as last time
					if (id_bit_number < LastDiscrepancy)
						search_direction = ((ROM_NO[rom_byte_number]
								& rom_byte_mask) > 0);
					else
						// if equal to last pick 1, if not then pick 0
						search_direction = (id_bit_number == LastDiscrepancy);

					// if 0 was picked then record its position in LastZero
					if (search_direction == 0) {
						last_zero = id_bit_number;

						// check for Last discrepancy in family
						if (last_zero < 9)
							LastFamilyDiscrepancy = last_zero;
					}
				}

				// set or clear the bit in the ROM byte rom_byte_number
				// with mask rom_byte_mask
				if (search_direction == 1)
					ROM_NO[rom_byte_number] |= rom_byte_mask;
				else
					ROM_NO[rom_byte_number] &= ~rom_byte_mask;

				// serial number search direction write bit
				OWWriteBit(search_direction);

				// increment the byte counter id_bit_number
				// and shift the mask rom_byte_mask
				id_bit_number++;
				rom_byte_mask <<= 1;

				// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
				if (rom_byte_mask == 0) {
					docrc8(ROM_NO[rom_byte_number]); // accumulate the CRC
					rom_byte_number++;
					rom_byte_mask = 1;
				}
			}
		} while (rom_byte_number < 8); // loop until through all ROM bytes 0-7

		// if the search was successful then
		if (!((id_bit_number < 65) || (crc8 != 0))) {
			// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
			LastDiscrepancy = last_zero;

			// check for last device
			if (LastDiscrepancy == 0)
				LastDeviceFlag = TRUE;

			search_result = TRUE;
		}
	}

	// if no device found then reset counters so next 'search' will be like a first
	if (!search_result || !ROM_NO[0]) {
		LastDiscrepancy = 0;
		LastDeviceFlag = FALSE;
		LastFamilyDiscrepancy = 0;
		search_result = FALSE;
	}

	return search_result;
}

uint8_t OWVerify(void)
{
	uint8_t rom_backup[8];
	uint8_t i, rslt, ld_backup, ldf_backup, lfd_backup;

	// keep a backup copy of the current state
	for (i = 0; i < 8; i++)
		rom_backup[i] = ROM_NO[i];
	ld_backup = LastDiscrepancy;
	ldf_backup = LastDeviceFlag;
	lfd_backup = LastFamilyDiscrepancy;

	// set search to find the same device
	LastDiscrepancy = 64;
	LastDeviceFlag = FALSE;

	if (OWSearch()) {
		// check if same device found
		rslt = TRUE;
		for (i = 0; i < 8; i++) {
			if (rom_backup[i] != ROM_NO[i]) {
				rslt = FALSE;
				break;
			}
		}
	} else
		rslt = FALSE;

	// restore the search state
	for (i = 0; i < 8; i++)
		ROM_NO[i] = rom_backup[i];
	LastDiscrepancy = ld_backup;
	LastDeviceFlag = ldf_backup;
	LastFamilyDiscrepancy = lfd_backup;

	// return the result of the verify
	return rslt;
}

void OWTargetSetup(uint8_t family_code)
{
	uint8_t i;
	// set the search state to find SearchFamily type devices
	ROM_NO[0] = family_code;
	for (i = 1; i < 8; i++)
		ROM_NO[i] = 0;
	LastDiscrepancy = 64;
	LastFamilyDiscrepancy = 0;
	LastDeviceFlag = FALSE;
}

void OWFamilySkipSetup(void)
{
	// set the Last discrepancy to last family discrepancy
	LastDiscrepancy = LastFamilyDiscrepancy;
	LastFamilyDiscrepancy = 0;

	// check for end of list
	if (LastDiscrepancy == 0)
		LastDeviceFlag = TRUE;
}

void OWInit(void)
{
	OW_HI;
}

uint8_t OWReset(void)
{
	OW_LOW;
	_delay_us(480);
	OW_HI;
	_delay_us(50);
	if (OW)
		return 0;
	_delay_us(480);
	return 1;
}

void OWWriteByte(uint8_t byte_value)
{
	for (uint8_t i = 0; i < 8; i++)
	{
		OWWriteBit(byte_value);
		byte_value >>= 1;
		_delay_us(2);
	}
	_delay_us(10);
}

void OWWriteBit(uint8_t bit_value)
{
	cli();
	OW_LOW;
	_delay_us(2);
	if (bit_value & 0x01)
		OW_HI;
	_delay_us(70);
	OW_HI;
	sei();
}


uint8_t OWReadByte(void)
{
	uint8_t val = 0;
	for (uint8_t i = 0; i < 8; i++)
		val |= OWReadBit() << i;
	return val;
}

uint8_t OWReadBit(void)
{
	uint8_t val = 0;
	cli();
	OW_LOW;
	_delay_us(2);
	OW_HI;
	_delay_us(15);
	if (OW)
		val = 1;
	_delay_us(60);
	sei();
	return val;
}

prog_uint8_t dscrc_table[] = {
		0, 94, 188, 226, 97, 63, 221, 131, 194, 156,
		126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1,
		227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225,
		191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124,
		34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197,
		132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88,
		25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184,
		230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199,
		37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237,
		179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243,
		112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19,
		77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108,
		142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202,
		148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87,
		9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
		233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246,
		168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137,
		107, 53
};

uint8_t docrc8(uint8_t value)
{
	crc8 = pgm_read_byte(&dscrc_table[crc8 ^ value]);
	return crc8;
}

#endif
